﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;

using System.Xml;


namespace Transformer.iHunter.UI
{
    public abstract partial class HtmlMessageEditorBase : UserControl, IFieldTextBoxOwner
    {        
        protected bool modified = false;

        protected Control lastControlFocused = null;
        protected bool plainTextMode = false;        

        protected static readonly string[] predefinedFields = new string[] { "Receiver", "Title", "Category" };

        protected readonly string[] fields;
        protected readonly string[] readonlyFieldsOnReply;
        protected readonly Dictionary<string, FieldTextBox> txtBoxes;        

        protected bool needNotifyMsgInvalidReason = false;

        protected int attachmentNum = 0;

        public HtmlMessageEditorBase()
        {
            InitializeComponent();

            this.fields = GetFields();
            this.readonlyFieldsOnReply = ReadonlyFieldsOnReply();
            this.txtBoxes = new Dictionary<string, FieldTextBox>();

            needNotifyMsgInvalidReason = false;
            attachmentNum = 0;
                                   
            htmlTextBox_Edit.HtmlEditorMouseUp += this.OnBrowserMouseUp;
            htmlTextBox_Edit.HtmlEditorEnter += this.OnBrowserEnterd;
            //htmlTextBox_Edit.HtmlEditorLeave += this.OnBrowserLeave;

            tableLayoutPanel_Fields.Controls.Clear();
            
            tableLayoutPanel_Fields.RowStyles.Clear();
            tableLayoutPanel_Fields.RowCount = fields.Length;
            
            for (int i =0; i < fields.Length; i++)            
            {
                tableLayoutPanel_Fields.RowStyles.Add(new RowStyle(SizeType.AutoSize));

                string field = fields[i];
                Label label = new Label();
                label.Text = field+" : ";
                label.Dock = DockStyle.Fill;
                label.BackColor = Color.Transparent;
                label.TextAlign = ContentAlignment.MiddleLeft;
                
                tableLayoutPanel_Fields.Controls.Add(label, 1, i);
                               

                FieldTextBox txtBox = new FieldTextBox(this, field);
                txtBox.Dock = DockStyle.Fill;               

                txtBoxes[field] = txtBox;
                tableLayoutPanel_Fields.Controls.Add(txtBox, 2, i);
            }

                       
            //textBox_Receiver.Focus();

            ResumeLayout(true);

            splitContainer_Edit.Location = new Point(
                10,
                tableLayoutPanel_Fields.Location.Y+tableLayoutPanel_Fields.Height+3
                );

            splitContainer_Edit.Size = new Size(
                this.Size.Width-25, this.Size.Height-tableLayoutPanel_Fields.Height-10
                );
            
        }

        protected abstract string[] GetFields();
        protected abstract string[] ReadonlyFieldsOnReply();

        protected void ShowMessageInvalidReason(string field, string reason)
        {
            if (needNotifyMsgInvalidReason)
            {
                txtBoxes[field].ShowTip(reason);
            }
        }
        

        #region IMessageEditor Members

        public IMessage GetMessage()
        {
            needNotifyMsgInvalidReason = true;
            bool msgValid = MessageValid;
            needNotifyMsgInvalidReason = false;

            if (msgValid)
            {
                Message msg = new Message();

                foreach (string field in fields)
                {
                    string str = txtBoxes[field].Text;
                    if (null == str)
                        str = string.Empty;
                    switch (field)
                    {
                        case "Receiver":
                            msg.Receiver = str;
                            break;

                        case "Title":
                            msg.Title = str;
                            break;

                        case "Category":
                            msg.Category = str;
                            break;

                        default:
                            msg.CustomFields[field] = str;
                            break;
                    }
                }

                msg.Content = htmlTextBox_Edit.HtmlText;

                return msg;
            }
            else
                return null;
        }

        public IMessage GetDraft()
        {
            Message msg = new Message();
            foreach (string field in fields)
            {
                string str = txtBoxes[field].Text;
                if (null == str)
                    str = string.Empty;
                switch (field)
                {
                    case "Receiver":
                        msg.Receiver = str;
                        break;

                    case "Title":
                        msg.Title = str;
                        break;

                    case "Category":
                        msg.Category = str;
                        break;

                    default:
                        msg.CustomFields[field] = str;
                        break;
                }
            }
            msg.Content = htmlTextBox_Edit.HtmlText;

            return msg;
        }

        public bool ImportMessage(IMessage source)
        {
            System.Diagnostics.Debug.Assert(null != source);

            attachmentNum = source.Attachments.Count;

            foreach (string field in fields)
            {
                string str;
                switch (field)
                {
                    case "Receiver":
                        str = source.Receiver;
                        break;

                    case "Title":
                        str = source.Title;
                        break;

                    case "Category":
                        str = source.Category;
                        break;

                    default:
                        if (source.CustomFields.ContainsKey(field))
                            str = source.CustomFields[field];
                        else
                            str = string.Empty;
                        break;
                }

                txtBoxes[field].Text = str;
            }

            htmlTextBox_Edit.HtmlText = source.Content;

            if (source.ReplyGuid != Guid.Empty)
            {
                foreach (string field in fields)
                {
                    foreach (string fixedField in readonlyFieldsOnReply)
                    {
                        if (fixedField.Equals(field))
                        {
                            
                            txtBoxes[field].ReadOnly = true;
                        }
                    }
                }
            }

            modified = false;            

            return MessageValid;
        }

        public bool MessageModified
        {
            get { return modified; }
            set { modified = value; }
        }

        public abstract bool MessageValid
        {
            get;
        }

      
        public System.Xml.XPath.XPathNavigator ContextualCommands
        {
            get
            {
                XmlDocument doc = new XmlDocument();
                doc.LoadXml(Properties.Resources.HtmlMessageEditorUI);

                return doc.CreateNavigator();
            }
        }

        public void ExcuteCommand(CommandUIEventArgs uiElement)
        {
            try
            {
                switch (uiElement.Name)
                {
                    case "Edit.Paste":
                        if (lastControlFocused is TextBox)
                            (lastControlFocused as TextBox).Paste();                       
                        else if (lastControlFocused == htmlTextBox_Edit)
                            htmlTextBox_Edit.Paste();
                        
                        break;

                    case "Edit.Copy":
                        if (lastControlFocused is TextBox)
                            (lastControlFocused as TextBox).Copy();
                        else if (lastControlFocused == htmlTextBox_Edit)
                            htmlTextBox_Edit.Copy();
                       
                        break;

                    case "Edit.Cut":
                        if (lastControlFocused is TextBox)
                            (lastControlFocused as TextBox).Cut();
                        else if (lastControlFocused == htmlTextBox_Edit)
                            htmlTextBox_Edit.Cut();
                        
                        break;

                    case "Edit.Undo":
                        if (lastControlFocused is TextBox)
                            (lastControlFocused as TextBox).Undo();
                        else if (lastControlFocused == htmlTextBox_Edit)
                            htmlTextBox_Edit.Undo();
                        
                        break;

                    case "Edit.Redo":
                        htmlTextBox_Edit.Redo();
                        break;

                    case "Font.Family":
                        {
                            string fontName = uiElement.Value as string;
                            if (null != fontName)
                                htmlTextBox_Edit.SetFont(fontName);
                        }
                        break;

                    case "Font.HtmlFontSize":
                        htmlTextBox_Edit.SetFontSize(int.Parse(uiElement.Value as string));
                        break;

                    case "Font.ForeColor":
                        {
                            Color c = (Color)uiElement.Value;
                            htmlTextBox_Edit.SetForeColor(c.R, c.G, c.B);
                        }
                        break;

                    case "Font.BackColor":
                        {
                            Color c = (Color)uiElement.Value;
                            htmlTextBox_Edit.SetBackColor(c.R, c.G, c.B);
                        }
                        break;

                    case "Font.Bold":
                        htmlTextBox_Edit.Bold();
                        break;

                    case "Font.Italic":
                        htmlTextBox_Edit.Italic();
                        break;

                    case "Font.Underline":
                        htmlTextBox_Edit.Underline();
                        break;

                    case "Font.Strikethrough":
                        htmlTextBox_Edit.StrikeThrough();
                        break;

                    case "Font.ClearFormat":
                        htmlTextBox_Edit.ClearFormat();
                        break;

                    case "Font.Superscript":
                        htmlTextBox_Edit.Superscript();
                        break;

                    case "Font.Subscript":
                        htmlTextBox_Edit.Subscript();
                        break;

                    case "Paragraph.LeftAlign":
                        if (uiElement.Checked)
                            htmlTextBox_Edit.JustifyNone();
                        else
                            htmlTextBox_Edit.JustifyLeft();
                        break;

                    case "Paragraph.RightAlign":
                        if (uiElement.Checked)
                            htmlTextBox_Edit.JustifyNone();
                        else
                            htmlTextBox_Edit.JustifyRight();
                        break;

                    case "Paragraph.CenterAlign":
                        if (uiElement.Checked)
                            htmlTextBox_Edit.JustifyNone();
                        else
                            htmlTextBox_Edit.JustifyCenter();
                        break;

                    case "Paragraph.Numbers":
                        htmlTextBox_Edit.InsertOrderedList();
                        break;

                    case "Paragraph.Bullets":
                        htmlTextBox_Edit.InsertUnOrderedList();
                        break;

                    case "Paragraph.Indent":
                        htmlTextBox_Edit.Indent();
                        break;

                    case "Paragraph.Unindent":
                        htmlTextBox_Edit.Outdent();
                        break;

                    case "Edit.Html":
                        SetPlainTextMode(false);
                        break;


                    case "Edit.PlainText":
                        SetPlainTextMode(true);
                        break;
                }

                if (lastControlFocused == htmlTextBox_Edit)
                    ToUpdateUI(200);
                else if (lastControlFocused is TextBox)
                    EnableEditButtons(lastControlFocused as TextBox);

            }
            catch { }

        }    
        

        public event CommandUIEventHandler UpdateCommandUI;

        #endregion
       
        private void EnableAll(bool enable)
        {
            if (UpdateCommandUI != null)
            {
                string[] buttons = new string[]
                {
                      "Font.Family",
                      "Font.HtmlFontSize",
                      "Font.Bold",
                      "Font.Italic",
                      "Font.Underline",
                      "Font.Strikethrough",  
                      "Font.ClearFormat",
                      "Font.Superscript",
                      "Font.Subscript",
                      "Font.ForeColor",
                      "Font.BackColor",
                      "Paragraph.LeftAlign",
                      "Paragraph.RightAlign",
                      "Paragraph.CenterAlign",  
                      "Paragraph.Numbers",
                      "Paragraph.Bullets",  
                      "Paragraph.Indent",
                      "Paragraph.Unindent"
                };

                foreach (string btn in buttons)
                {
                    CommandUIEventArgs updateArgs = new CommandUIEventArgs();
                    updateArgs.Name = btn;
                    updateArgs.Enabled = enable;
                    updateArgs.Mask = CommandUIMask.None | CommandUIMask.Enabled;

                    this.UpdateCommandUI(this, updateArgs);
                }               
               
            }
        }

      
             
        private void OnBrowserEnterd(object sender, EventArgs args)
        {
            lastControlFocused = htmlTextBox_Edit;

            EnableAll(true);

            ToUpdateUI(200);
        }

        private void OnBrowserLeave(object sender, EventArgs args)
        {
            DisableEditButtons();
        }

        private void OnBrowserMouseUp(object sender, EventArgs args)
        {
            ToUpdateUI(100);
        }

        private void UpdateButtonStates()
        {
            if (this.UpdateCommandUI != null)
            {
                bool cut = (bool)htmlTextBox_Edit.Document.InvokeScript("IsTextSelected");
                bool undo = (bool)htmlTextBox_Edit.Document.InvokeScript("CanUndo");
                bool redo = (bool)htmlTextBox_Edit.Document.InvokeScript("CanRedo");

                EnableClipboardButtons(cut, undo, redo);

                CommandUIEventArgs updateArgs = new CommandUIEventArgs();
                updateArgs.Name = "Font.Family";
                updateArgs.Mask = CommandUIMask.None | CommandUIMask.Value;
                updateArgs.Value = (htmlTextBox_Edit.Document.InvokeScript("GetFont") ?? string.Empty).ToString();
                this.UpdateCommandUI(this, updateArgs);

                updateArgs.Name = "Font.HtmlFontSize";
                updateArgs.Value = htmlTextBox_Edit.Document.InvokeScript("GetFontSize").ToString();
                this.UpdateCommandUI(this, updateArgs);

                updateArgs = new CommandUIEventArgs();
                updateArgs.Mask = CommandUIMask.None | CommandUIMask.Checked;
                updateArgs.Name = "Font.Bold";
                updateArgs.Checked = (bool)htmlTextBox_Edit.Document.InvokeScript("IsBold");
                this.UpdateCommandUI(this, updateArgs);

                updateArgs = new CommandUIEventArgs();
                updateArgs.Mask = CommandUIMask.None | CommandUIMask.Checked;
                updateArgs.Name = "Font.Italic";
                updateArgs.Checked = (bool)htmlTextBox_Edit.Document.InvokeScript("IsItalic");
                this.UpdateCommandUI(this, updateArgs);

                updateArgs = new CommandUIEventArgs();
                updateArgs.Mask = CommandUIMask.None | CommandUIMask.Checked;
                updateArgs.Name = "Font.Underline";
                updateArgs.Checked = (bool)htmlTextBox_Edit.Document.InvokeScript("IsUnderline");
                this.UpdateCommandUI(this, updateArgs);

                updateArgs = new CommandUIEventArgs();
                updateArgs.Mask = CommandUIMask.None | CommandUIMask.Checked;
                updateArgs.Name = "Font.Strikethrough";
                updateArgs.Checked = (bool)htmlTextBox_Edit.Document.InvokeScript("IsStrikethrough");
                this.UpdateCommandUI(this, updateArgs);

                updateArgs = new CommandUIEventArgs();
                updateArgs.Mask = CommandUIMask.None | CommandUIMask.Checked;
                updateArgs.Name = "Font.Superscript";
                updateArgs.Checked = (bool)htmlTextBox_Edit.Document.InvokeScript("IsSuperscript");
                this.UpdateCommandUI(this, updateArgs);

                updateArgs = new CommandUIEventArgs();
                updateArgs.Mask = CommandUIMask.None | CommandUIMask.Checked;
                updateArgs.Name = "Font.Subscript";
                updateArgs.Checked = (bool)htmlTextBox_Edit.Document.InvokeScript("IsSubscript");
                this.UpdateCommandUI(this, updateArgs);

                updateArgs = new CommandUIEventArgs();
                updateArgs.Mask = CommandUIMask.None | CommandUIMask.Checked;
                updateArgs.Name = "Paragraph.LeftAlign";
                updateArgs.Checked = (bool)htmlTextBox_Edit.Document.InvokeScript("IsJustifyLeft");
                this.UpdateCommandUI(this, updateArgs);

                updateArgs = new CommandUIEventArgs();
                updateArgs.Mask = CommandUIMask.None | CommandUIMask.Checked;
                updateArgs.Name = "Paragraph.RightAlign";
                updateArgs.Checked = (bool)htmlTextBox_Edit.Document.InvokeScript("IsJustifyRight");
                this.UpdateCommandUI(this, updateArgs);

                updateArgs = new CommandUIEventArgs();
                updateArgs.Mask = CommandUIMask.None | CommandUIMask.Checked;
                updateArgs.Name = "Paragraph.CenterAlign";
                updateArgs.Checked = (bool)htmlTextBox_Edit.Document.InvokeScript("IsJustifyCenter");
                this.UpdateCommandUI(this, updateArgs);

                updateArgs = new CommandUIEventArgs();
                updateArgs.Mask = CommandUIMask.None | CommandUIMask.Checked;
                updateArgs.Name = "Paragraph.Numbers";
                updateArgs.Checked = (bool)htmlTextBox_Edit.Document.InvokeScript("IsOrderedList");
                this.UpdateCommandUI(this, updateArgs);

                updateArgs = new CommandUIEventArgs();
                updateArgs.Mask = CommandUIMask.None | CommandUIMask.Checked;
                updateArgs.Name = "Paragraph.Bullets";
                updateArgs.Checked = (bool)htmlTextBox_Edit.Document.InvokeScript("IsBulletList");
                this.UpdateCommandUI(this, updateArgs);
            }
        }

        private void ToUpdateUI(int interval)
        {
            timer_Update.Stop();

            timer_Update.Interval = interval;

            timer_Update.Start();

            modified = true;
        }

        private void htmlTextBox_Edit_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
        {
            if (e.KeyCode == Keys.Delete)
            {
                htmlTextBox_Edit.Delete();
            }            

            ToUpdateUI(300);
        }

        private void timer_Update_Tick(object sender, EventArgs e)
        {
            //System.Diagnostics.Debug.WriteLine("Update UI");
            timer_Update.Stop();

            UpdateButtonStates();

            
        }

        private void EnableTextModeButtons(bool isPlainText)
        {
            if (this.UpdateCommandUI != null)
            {
                CommandUIEventArgs updateArgs = new CommandUIEventArgs();
                updateArgs.Name = "Edit.Html";
                updateArgs.Mask = CommandUIMask.Checked;
                //updateArgs.Enabled = isPlainText;
                updateArgs.Checked = !isPlainText;

                this.UpdateCommandUI(this, updateArgs);

                updateArgs.Name = "Edit.PlainText";
                //updateArgs.Enabled = isPlainText;
                updateArgs.Checked = isPlainText;
                this.UpdateCommandUI(this, updateArgs);
            }
        }

        private void SetPlainTextMode(bool isPlainText)
        {
            if (PlainTextMode == isPlainText)
                return;

            this.plainTextMode = isPlainText;
            if (!isPlainText)
            {
                splitContainer_Edit.Panel2Collapsed = true;
                
                htmlTextBox_Edit.HtmlText = "";
                if (null != textBox_Text.Text && textBox_Text.Text.Length > 0)
                {
                    string str = Clipboard.GetText();

                    textBox_Text.SelectAll();
                    textBox_Text.Copy();
                    htmlTextBox_Edit.Paste();

                    if (null != str)
                        Clipboard.SetText(str);
                }

                OnBrowserEnterd(this, EventArgs.Empty);

                EnableTextModeButtons(isPlainText);
            }
            else
            {
                bool htmlAlreadyExist = htmlTextBox_Edit.HtmlText.Length > 0;
                /*if (htmlAlreadyExist)
                {
                    if (MessageBox.Show("Plain text may lose all formats, do you want to continue?", "warning", MessageBoxButtons.YesNo)
                        != DialogResult.Yes)
                        return;
                }*/

                timer_Update.Stop();
                splitContainer_Edit.Panel1Collapsed = true;

                textBox_Text.Text = "";

                if (htmlAlreadyExist)
                {
                    string str = Clipboard.GetText();

                    htmlTextBox_Edit.SelectAll();
                    htmlTextBox_Edit.Copy();

                    textBox_Text.Paste();

                    if (null != str)
                        Clipboard.SetText(str);
                }

                lastControlFocused = textBox_Text;
                textBox_Text.Focus();
                if (null != textBox_Text.Text)
                textBox_Text.SelectionStart = textBox_Text.Text.Length;
                EnableClipboardButtons(textBox_Text.SelectionLength > 0, textBox_Text.CanUndo, false);
                EnableAll(false);

                EnableTextModeButtons(isPlainText);
            }

            
        }

        private bool PlainTextMode
        {
            get 
            {
                return plainTextMode;
            }
        }

        private void textBox_Text_TextChanged(object sender, EventArgs e)
        {
            modified = true;
        }

        private void textBox_Text_KeyUp(object sender, KeyEventArgs e)
        {
            EnableClipboardButtons(textBox_Text.SelectionLength > 0, textBox_Text.CanUndo, false);
        }

        private void textBox_Text_Enter(object sender, EventArgs e)
        {
            lastControlFocused = textBox_Text;
        }

        private void textBox_Text_Click(object sender, EventArgs e)
        {
            EnableClipboardButtons(textBox_Text.SelectionLength > 0, textBox_Text.CanUndo, false);
        }

        private void DisableEditButtons()
        {
            if (this.UpdateCommandUI != null)
            {
                CommandUIEventArgs updateArgs = new CommandUIEventArgs();
                updateArgs.Name = "Edit.Cut";
                updateArgs.Enabled = false;
                updateArgs.Mask = CommandUIMask.None | CommandUIMask.Enabled;

                this.UpdateCommandUI(this, updateArgs);

                updateArgs.Name = "Edit.Copy";
                this.UpdateCommandUI(this, updateArgs);

                updateArgs.Name = "Edit.Paste";
                updateArgs.Enabled = false;
                this.UpdateCommandUI(this, updateArgs);

                updateArgs.Name = "Edit.Undo";
                updateArgs.Enabled = false;
                this.UpdateCommandUI(this, updateArgs);

                updateArgs.Name = "Edit.Redo";
                updateArgs.Enabled = false;
                this.UpdateCommandUI(this, updateArgs);
            }
        }

        public void EnableEditButtons(TextBox txtBox)
        {
            EnableClipboardButtons(txtBox.SelectionLength > 0, txtBox.CanUndo, false);
        }

        public void EnableClipboardButtons(bool cut, bool undo, bool redo)
        {
            if (this.UpdateCommandUI != null)
            {
                CommandUIEventArgs updateArgs = new CommandUIEventArgs();
                updateArgs.Name = "Edit.Cut";
                updateArgs.Enabled = cut;
                updateArgs.Mask = CommandUIMask.None | CommandUIMask.Enabled;

                this.UpdateCommandUI(this, updateArgs);

                updateArgs.Name = "Edit.Copy";
                this.UpdateCommandUI(this, updateArgs);

                updateArgs.Name = "Edit.Paste";
                updateArgs.Enabled = Clipboard.ContainsText();
                this.UpdateCommandUI(this, updateArgs);

                updateArgs.Name = "Edit.Undo";
                updateArgs.Enabled = undo;
                this.UpdateCommandUI(this, updateArgs);

                updateArgs.Name = "Edit.Redo";
                updateArgs.Enabled = redo;
                this.UpdateCommandUI(this, updateArgs);
            }
        }              
        
        public void EnterFieldTextBox(FieldTextBox txtBox)
        {
            lastControlFocused = txtBox;
            if (!PlainTextMode)
            {
                timer_Update.Stop();

                EnableAll(false);
            }
        }

        #region IFieldTextBoxOwner Members


        public void MarkMessageModified()
        {
            this.modified = true;
        }

        #endregion

        private void HtmlMessageEditorBase_Load(object sender, EventArgs e)
        {
        }

        

        private void HtmlMessageEditorBase_VisibleChanged(object sender, EventArgs e)
        {
        }

        private void HtmlMessageEditorBase_ParentChanged(object sender, EventArgs e)
        {
            if (this.Visible)
            {
                EnableAll(false);
                DisableEditButtons();
                EnableTextModeButtons(false);
            }
        }

        private void textBox_Text_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Escape)
            {
                e.Handled = true;
            }
            else if (e.KeyCode == Keys.Enter)
            {
                e.Handled = true;
                textBox_Text.SelectedText = System.Environment.NewLine;

            }
        }
    }
}
